/* Copyright © 2010 LPRM
Universidade Federal do Espírito Santo - RNP-GT CWTools

This file is part of JoinUs!.

JoinUs! is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

JoinUs! is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with JoinUs!. If not, see <http://www.gnu.org/licenses/>. */
package br.ufes.inf.joinus;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.isisufes.joinus.appserver.Base64;

/**
 * Servlet implementation class Proxy
 */
public final class Proxy extends HttpServlet {
	private static final long serialVersionUID = 1L;
	static Map<String, SecretKey> keys = null;
	final static private String urlJoinUsServer = "joinus.inf.ufes.br";
	final static private int portJoinUsServer = 8080;
	final static private int portSSLJoinUsServer = 443;
	static private Socket SSLSocket;
	static private SSLContext sc = null;	
	static final public int OK = 0;
	static final public int SERVER_ERROR = 1;
	static final public int SSL_ERROR = 2;
	static final public  int INVALID_USER = 4;
	static BufferedWriter bw;
	static BufferedReader br;
	
	private String servletPath;
	private Map<String, String> params;
	private Map<String, String> secureParams;
	private String userId;
	private String strResponse;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Proxy() {
        super();
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		if(request.getParameter("login") != null){
			doLogin(request, response);
		}else if(request.getParameter("registrar") != null){
			registerUser(request, response);
		}else if(request.getParameter("campanhas") != null){
			doListarCampanhas(request, response);
		}else if(request.getParameter("campanha") != null){
			doCadastrarCampanha(request, response);
		}
	}

	private void doCadastrarCampanha(HttpServletRequest request, HttpServletResponse response) {
		String id = request.getParameter("userId");
		String name = request.getParameter("name");
		String desc = request.getParameter("desc");
		String posX = request.getParameter("posX");
		String posY = request.getParameter("posY");
		String radius = request.getParameter("radius");
		String exp = request.getParameter("exp");
		String pub = request.getParameter("pub");
		
		if(id != null && name != null && desc != null && posX != null && posY != null && radius != null && exp != null && pub != null){
			params = new HashMap<String, String>();
			params.put("id", id);
			
			secureParams = new HashMap<String, String>();
			secureParams.put("action", "createCampaign");
			secureParams.put("name", name);
			secureParams.put("description", desc);
			secureParams.put("posX", posX);
			secureParams.put("posY", posY);
			secureParams.put("radius", radius);
			secureParams.put("expiration", exp);
			secureParams.put("public", pub);
			servletPath = "/servlets/Campaign";
			
			doConnection(request, response);
		}else{
			try{
				response.getOutputStream().println("(EE) Criação de campanha - Parâmetros inválidos!");
			}catch(Exception e){
				System.out.println("Erro na escrita do OutputStream.");
			}
		}
		
	}

	private void doListarCampanhas(HttpServletRequest request, HttpServletResponse response) {
		String id = request.getParameter("userId");
		String camp = request.getParameter("campaignId");
		
		if(id != null && camp != null){
			params = new HashMap<String, String>();
			params.put("id", id);
			
			secureParams = new HashMap<String, String>();
			secureParams.put("action", "getCampaigns");
			servletPath = "/servlets/Campaign";
			
			doConnection(request, response);
		}else{
			try{
				response.getOutputStream().println("(EE) Listagem de campanhas - Parâmetros inválidos!");
			}catch(Exception e){
				System.out.println("Erro na escrita do OutputStream.");
			}
		}
		
	}

	private void registerUser(HttpServletRequest request, HttpServletResponse response) {
		String id = request.getParameter("userId");
		String key = request.getParameter("userKey");
		
		SecretKey objKey = new SecretKeySpec(Base64.decode(key), "DES");
		keys.put(id, objKey);

		try{
			response.getOutputStream().println("OK");
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void doLogin(HttpServletRequest request, HttpServletResponse response) {
		String login = request.getParameter("user");
		String senha = request.getParameter("pass");
		
		if(login != null && senha != null){
			params = new HashMap<String, String>();
			secureParams = new HashMap<String, String>();
			params.put("action", "login");
			params.put("login", login);
			params.put("password", senha);
			servletPath = "/servlets/Login";
			
			doConnection(request, response);
			
			try{
				response.getOutputStream().print(strResponse);
			}catch(Exception e){}
		}else{
			try{
				response.getOutputStream().println("(EE) Login - Parâmetros inválidos!");
			}catch(Exception e){
				System.out.println("Erro na escrita do OutputStream.");
			}
		}		
	}

	private void doConnection(HttpServletRequest request, HttpServletResponse response) {
		if(!secureParams.isEmpty()){
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
	        ObjectOutputStream oos;
			try {
				oos = new ObjectOutputStream(bos);
		        oos.writeObject(secureParams);
		
		        Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
		        cipher.init(Cipher.ENCRYPT_MODE, keys.get(userId));
		        
		        String msg = Base64.encode(cipher.doFinal(bos.toByteArray()));
				
		        params.put("msg", msg);	
	        
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		StringBuilder sb = new StringBuilder("");

		try {
			setSSLContext();

			bw.append("GET " + servletPath + getParams(params) + "\n");
			bw.append("Host: " + urlJoinUsServer+ ":" + portJoinUsServer + "\n\n");
			bw.flush();
			
			String cl;
			while ((cl = br.readLine()) != null){
				sb.append(cl);
			}
		} catch (Exception e) {
			try{
				response.getOutputStream().println("(EE) Proxy - Falha na conexão.");
			}catch(Exception e1){
			}
			
			return;
		}	
		
		strResponse = sb.toString();
		
		params.clear();
		secureParams.clear();
	}

	private String getParams(Map<String, String> params) {
		String ret = "";
		
		for( Entry<String, String> e: params.entrySet()){
			ret = ret + "&" + e.getKey() + "=" + e.getValue();
		}
		return ret.substring(1);
	}

	static public int setSSLContext() {
		try {
			FileInputStream arqCertificado = new FileInputStream("/res/certificate.crt");
			byte b[] = new byte[100];
			arqCertificado.read(b);
			System.out.println(b);
			
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			
			X509Certificate cert = (X509Certificate) cf.generateCertificate(arqCertificado);

			KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
			ks.load(null);
			ks.setCertificateEntry("cert", cert);

			String ka = KeyManagerFactory.getDefaultAlgorithm();
			String ta = TrustManagerFactory.getDefaultAlgorithm();

			KeyManagerFactory kmf = KeyManagerFactory.getInstance(ka);
			TrustManagerFactory tmf = TrustManagerFactory.getInstance(ta);

			kmf.init(ks, null);
			tmf.init(ks);

			KeyManager[] kms = kmf.getKeyManagers();
			TrustManager[] tms = tmf.getTrustManagers();

			sc = SSLContext.getInstance("TLS");
			sc.init(kms, tms, new java.security.SecureRandom());

			SSLSocket = sc.getSocketFactory().createSocket(urlJoinUsServer, portSSLJoinUsServer);
			
			bw = new BufferedWriter(new OutputStreamWriter(SSLSocket.getOutputStream()));			
			br = new BufferedReader(new InputStreamReader(SSLSocket.getInputStream()));
		} catch (Exception e) {
			System.out.println("Erro no setup do SSL");
			return SSL_ERROR;
		}

		return OK;
	}
}
